﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_OCF_Parser.h".
//-------------------------------------------------------------------

#ifndef _UXS_PARSER_INTERFACE_H
#define _UXS_PARSER_INTERFACE_H

#pragma once

#include "ConfigTypes.h"

namespace UX_STL
{
namespace UXParser
{


	//
	// Parser Interface
	//

	class UX_EXPORT IParser
	{
	public:
		virtual void  SetDirectories (const string_array_t &aDir) = 0;
		virtual bool  PreParseFile (uni_c_string sFileName) = 0;
		virtual bool  ParseFile (RFilePtr pFile, config_node_t pNode, uint uSize = 0) = 0;
		virtual bool  Save (WFilePtr pFile, config_const_node_t pNode) = 0;
		virtual bool  IsSupported (uni_c_string sFileExt) = 0;
		virtual const string_array_t & GetLog () = 0;
		virtual void  ClearLog () = 0;
		virtual void  Free () = 0;
	};



	//
	// OCF Parser
	//
	
	class UX_EXPORT OCFParser : public IParser
	{
	public:
		struct TChunk
		{
			// types
			enum EOperation
			{
				// unary
				PLUS		= 1,
				MINUS,
				NOT,
				INV,
				_UNARY,
				// binary
				MUL,
				DIV,
				MOD,
				AND,
				OR,
				XOR,
				// logic
				_EQUAL,
				LESS,
				GREATHER,
				_BIN		= 0x00000800,
				_EQ			= 0x00001000,
				_AND		= 0x00002000,
				_OR			= 0x00004000,
				_LESS		= 0x00008000,
				_GREATHER	= 0x00010000,
				ADD			= PLUS | _BIN,
				SUB			= MINUS | _BIN,
				LOGIC_AND	= AND | _AND,
				LOGIC_OR	= OR | _OR,
				EQUAL		= _EQUAL | _EQ,
				NOT_EQUAL	= NOT | _EQ,
				LEQUAL		= LESS | _EQ,
				GEQUAL		= GREATHER | _EQ,
				L_SHIFT		= LESS | _LESS,
				R_SHIFT		= GREATHER | _GREATHER,
				_BINARY,
				// brackets
				L_BRACKET	= 0x00100000,
				R_BRACKET,
			};

			enum EType
			{
				UNKNOWN		= 0,
				VALUE,
				OPERATION,
			};


			// variables
			union {
				int64			value;
				struct {
					EOperation	op;
					uint		priority;
				};
			};
			EType				type;
			///
			TChunk () : type(UNKNOWN), value(0) {}
			TChunk (EOperation o) : op(o), priority(0), type(OPERATION) {}
			TChunk (int64 i) : value(i), type(VALUE) {}
		};

	private:
		struct EKeys
		{
			enum type
			{
				UNKNOWN	= 0,
				STATIC,
			};
		};

		struct TPreParsedFile
		{
			config_tree_t	sTypeTree;
			FileAddress		sFileName;
			bool			bParseEnd;	// true if successfuly parsed
			///
			TPreParsedFile (const string &name) : sFileName(name), bParseEnd(false) {}
		};

		struct TParsedFile
		{
			FileAddress		sFileName;
			bool			bOpened;
			///
			TParsedFile (const FileAddress &name) : sFileName(name), bOpened(true) {}
		};

		struct TParseData
		{
			config_node_t	pNode;
			string			sFilePath,
							sFieldName;
			const char *	pStr;
			usize			uLength;
			usize			uLine,
							uNumChars;
			int				iHeight;	// counter of {} brackets
			EKeys::type		eKeys;
			///
			TParseData () : pNode(), pStr(null), uLine(1), uNumChars(0), iHeight(0), uLength(0), eKeys(EKeys::UNKNOWN) {}
		};


		typedef array< TPreParsedFile >		preparsed_array_t;
		typedef array< TParsedFile >		file_array_t;
		
	private:
		preparsed_array_t	_aPreParsed;
		file_array_t		_aParsedFiles;
		string_array_t		_aLog,
							_aDirectories;
		
	private:
		bool _FindPreParsed ();
		bool _CompareFiles (RFilePtr pFile);
		bool _OpenFile (uni_c_string sName, RFilePtr &pFile);
		bool _FindInPreParsedFiles (const string &sName, const string &sPath, config_node_t pNode);
		
		// find
		bool _FindInNamespace (uni_c_string_array_t &aStr, CCfgBaseElement *&pObj, config_const_node_t pNode);
		bool _FindInEnum (uni_c_string_array_t &aStr, CCfgBaseElement *&pObj, config_const_node_t pNode);
		bool _FindConst (uni_c_string_array_t &aStr, CCfgBaseElement *&pObj, config_const_node_t pNode, uint uDepth = 0);
		bool _FindValue (TParseData &sData, uni_c_string_array_t &aIdent);
		
		// read
		bool _Parse (RFilePtr pFile, config_node_t pNode, uint uSize = 0);
		void _ProcessSpacesAndComments (TParseData &sData);
		void _ReadValue (TParseData &sData);
		void _ReadArray (TParseData &sData, e_config_object::type eType);
		void _ReadNumber (TParseData &sData);
		void _ReadString (TParseData &sData);
		void _ReadIdentifer (TParseData &sData, string &sIdent, uni_c_string_array_t &aNamespaces);
		void _ReadName (TParseData &sData, string &sIdent);
		void _ReadObjectData (TParseData &sData);
		void _ReadObject (TParseData &sData);
		void _ReadNamespace (TParseData &sData);
		void _ReadScript (TParseData &sData);
		void _ReadConst (TParseData &sData);
		void _ReadEnum (TParseData &sData);
		void _FindSemicolon (TParseData &sData);
		void _AttachFile (TParseData &sData);
		bool _SwitchIdentifer (TParseData &sData, const string &sIdent, bool bRightValue, bool bNoObj = false);

		// math
		void _ReadStringOp (TParseData &sData, CCfgBaseElement *pElem = null);
		void _ReadMathOper (TParseData &sData, CCfgBaseElement *pElem = null);
		void _ParseOperations (TParseData &sData, array<TChunk> &aChunks, usize uLastBracket);
		void _UnaryOperation (TParseData &sData, int64 &res, TChunk::EOperation op);
		void _UnaryOperation (TParseData &sData, double &res, TChunk::EOperation op);
		void _BinaryOperation (TParseData &sData, int64 &res, TChunk::EOperation op, const int64 &l, const int64 &r);
		void _BinaryOperation (TParseData &sData, double &res, TChunk::EOperation op, const double &l, const double &r);

		// save
		static void _s_SaveNumber (string &str, config_const_node_t pNode, int iHeight);
		static void _s_SaveString (string &str, config_const_node_t pNode, int iHeight);
		static void _s_SaveArray (string &str, config_const_node_t pNode, int iHeight);
		static void _s_SaveConst (string &str, config_const_node_t pNode, int iHeight);
		static void _s_SaveObject (string &str, config_const_node_t pNode, int iHeight);
		static void _s_SaveNamespace (string &str, config_const_node_t pNode, int iHeight);
		static void _s_SaveEnum (string &str, config_const_node_t pNode, int iHeight);
		static void _s_RecursiveSave (string &str, config_const_node_t pNode, int iHeight = 0);

	public:
		OCFParser ();
		~OCFParser ();
		
		void  SetDirectories (const string_array_t &aDir);
		bool  PreParseFile (uni_c_string sFileName);
		bool  ParseFile (RFilePtr pFile, config_node_t pNode, uint uSize = 0);
		bool  Save (WFilePtr pFile, config_const_node_t pNode);
		bool  IsSupported (uni_c_string sFileExt);
		const string_array_t & GetLog ()	{ return _aLog; }
		void  ClearLog ()					{ _aLog.Clear(); }
		void  Free ();
	};

//-------------------------------------------------------------------

}	// UXParser
}	// UX_STL

#endif	// _UXS_PARSER_INTERFACE_H